#pragma once

#include <iostream>
#include <algorithm>
#include "hdmap/math/Algebra.hpp"
#include "assert.h"
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif

namespace Navinfo
{
	namespace Engine
	{
		namespace Math
		{
			template< typename T >
			struct AxisAlignedBoxBase
			{
				typedef typename T::ValueType BaseValueType;

				T min;
				T max;

				AxisAlignedBoxBase< T >() : min(), max() {}
				AxisAlignedBoxBase< T >(const T &_min, const T &_max) : min(_min), max(_max) {}
				AxisAlignedBoxBase< T >(const BaseValueType &x1, const BaseValueType &y1, const BaseValueType &x2, const BaseValueType &y2)
					:min(std::min(x1, x2), std::min(y1, y2)),max(std::max(x1, x2), std::max(y1, y2)) {}

				bool operator==(const AxisAlignedBoxBase< T >& rhs) const
				{
					return ((*this).min == rhs.min) && ((*this).max == rhs.max);
				}

				bool operator!=(const AxisAlignedBoxBase< T >& rhs) const
				{
					return !(*this == rhs);
				}

				AxisAlignedBoxBase< T >& operator/=(const T& rhs)
				{
					(*this).min /= rhs;
					(*this).max /= rhs;
					return *this;
				}

				AxisAlignedBoxBase< T >& operator*=(const T& rhs)
				{
					(*this).min *= rhs;
					(*this).max *= rhs;
					return *this;
				}

				T Center() const
				{
					return ((*this).min + (*this).max) * T(0.5);
				}

				T Size() const
				{
					return (*this).max - (*this).min;
				}

			};

			///Calculates the union of two volumes
			template< typename T >
			AxisAlignedBoxBase< T > Union(const AxisAlignedBoxBase< T >& lhs, const AxisAlignedBoxBase< T >& rhs)
			{
				return AxisAlignedBoxBase< T >(Min(lhs.min, rhs.min), Max(lhs.max, rhs.max));
			}

			///Calculates the intersection of two volumes
			template< typename T >
			AxisAlignedBoxBase< T > Intersection(const AxisAlignedBoxBase< T >& lhs, const AxisAlignedBoxBase< T >& rhs)
			{
				return AxisAlignedBoxBase< T >(
					T(std::max(lhs.min.x_, rhs.min.x_), std::max(lhs.min.y_, rhs.min.y_)),
					T(std::min(lhs.max.x_, rhs.max.x_), std::min(lhs.max.y_, rhs.max.y_))
					);
			}

			template< typename T >
			bool InRange(const T &s, const T &e, const T &q){
				return s <= q && q <= e;
			}

			///Calculates if <location> is in <box>
			template< typename T >
			bool In(const AxisAlignedBoxBase< T >& box, const T& location)
			{
				//return (box.min.x_ <= location) && (location <= box.max);
				return (InRange(box.min.x_, box.max.x_, location.x_) && InRange(box.min.y_, box.max.y_, location.y_));
			}

			template< typename T >
			bool IsIntersection2d(const AxisAlignedBoxBase< T >& r1, const AxisAlignedBoxBase< T >& r2)
			{
				if (abs((r1.min.x_ + r1.max.x_) / 2 - (r2.min.x_ + r2.max.x_) / 2) < ((r1.max.x_ + r2.max.x_ - r1.min.x_ - r2.min.x_) / 2)
					&& abs((r1.min.y_ + r1.max.y_) / 2 - (r2.min.y_ + r2.max.y_) / 2) < ((r1.max.y_ + r2.max.y_ - r1.min.y_ - r2.min.y_) / 2))
					return true;
				return false;
			}

			template<typename T>
			std::vector<T> GetBoxBoundaryLine2d(const AxisAlignedBoxBase< T >& box){
				std::vector<T> ret;
				ret.push_back(box.min);
				ret.push_back(T(box.max.x_, box.min.y_));
				ret.push_back(box.max);
				ret.push_back(T(box.min.x_, box.max.y_));
				ret.push_back(box.min);
				return ret;
			}

			template< typename T >
			bool Disjoint(const AxisAlignedBoxBase< T >& lhs, const AxisAlignedBoxBase< T >& rhs)
			{
				return !(Math::In(lhs, rhs.min) || Math::In(lhs, rhs.max) ||
					Math::In(rhs, lhs.min) || Math::In(rhs, lhs.max));
			}

			template< typename T >
			std::ostream& operator<<(std::ostream& os, const AxisAlignedBoxBase< T >& box)
			{
				os << box.min << ':' << box.max;

				return os;
			}

			template< typename T >
			std::istream& operator>>(std::istream& is, AxisAlignedBoxBase< T >& box)
			{
				char ch;
				is >> box.min >> ch >> box.max;

				return is;
			}

			
		} //namespace Math

		namespace DataSource{
			template< typename T >
			struct CoordSegmentBase
			{
				CoordSegmentBase< T >(const T &start, const T &end) : start_(start), end_(end) { }

				const T &start() const { return start_; }

				const T &end() const { return end_; }

				double DistanceSquareTo(const T &point) const {
					assert(false); 
					return 0;
				}

				T start_;
				T end_;
			};
		}
	}
}